package com.wangijun.yuan.web.api;


import cloud.tianai.captcha.application.ImageCaptchaApplication;
import cloud.tianai.captcha.spring.plugins.secondary.SecondaryVerificationApplication;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.wangijun.yuan.web.base.CommonResult;
import com.wangijun.yuan.core.domain.UserAuth;
import com.wangijun.yuan.core.domain.UserInfo;
import com.wangijun.yuan.core.domain.UserSetting;
import com.wangijun.yuan.core.model.em.UserStatusEnum;
import com.wangijun.yuan.core.util.AesUtil;
import com.wangijun.yuan.web.model.em.DeviceEnum;
import com.wangijun.yuan.web.model.vo.TokenVO;
import com.wangijun.yuan.web.service.MailService;
import com.wangijun.yuan.web.util.YuanWebConst;
import com.wangijun.yuan.web.util.YuanWebUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;

@Tag(name = "3-认证接口")
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AuthController {
    private final MailService mailService;
    private final StringRedisTemplate stringRedisTemplate;
    private final ImageCaptchaApplication imageCaptchaApplication;


    @Getter
    @Setter
    static class LoginParam {
        private String loginName;
        private String passwd;
    }

    @Operation(summary = "密码登录", description = "密码登录")
    @PostMapping("/passwd-login")
    CommonResult<TokenVO> passwdLogin(@RequestBody LoginParam param) {

        UserInfo userInfo = Db.lambdaQuery(UserInfo.class)
                .eq(UserInfo::getEmail, param.loginName)
                .ne(UserInfo::getUserStatus, UserStatusEnum.del.getCode())
                .one();
        if (userInfo == null) {
            CommonResult.error("邮箱未注册！");
        }
        if (userInfo.getUserStatus().equals(UserStatusEnum.disable)) {
            CommonResult.error("邮箱已被禁用");
        }
        UserAuth userAuth = Db.lambdaQuery(UserAuth.class).eq(UserAuth::getUserId, userInfo.getId()).one();

        if (StrUtil.isBlank(userAuth.getPasswd())) {
            CommonResult.error("未设置密码！");
        }

        if (!AesUtil.dec(userAuth.getPasswd()).equals(userAuth.getSalt() + param.passwd)) {
            CommonResult.error("密码错误！");
        }
        StpUtil.login(userInfo.getId(), SaLoginModel.create()
                .setDevice(DeviceEnum.PC.name()));
        Db.lambdaUpdate(UserAuth.class)
                .eq(UserAuth::getUserId, userInfo.getId())
                .set(UserAuth::getLastLoginTime, DateUtil.date())
                .update();
        TokenVO tokenVo = BeanUtil.copyProperties(StpUtil.getTokenInfo(), TokenVO.class);
        tokenVo.setPowerList(StpUtil.getPermissionList());
        return CommonResult.data(tokenVo);
    }

    @Getter
    @Setter
    static class SendCodeParam {
        // 验证码返回ID
        private String id;
        // 邮箱
        private String loginName;
        // 发送邮件类别
        public static final String login = "login";
        public static final String changePd = "changePd";
        private String mark;
    }

    @Operation(summary = "发送验证码", description = "发送验证码")
    @PostMapping("/send-code")
    CommonResult<Boolean> sendCode(@RequestBody SendCodeParam param) {

        checkEmailAndCaptcha(param.id, param.loginName);
        if (param.mark.equals(SendCodeParam.login)) {
            mailService.sendLoginCode(param.getLoginName());
        } else {
            mailService.sendChangePdCode(param.getLoginName());
        }
        return CommonResult.ok("验证码发送成功");
    }


    /**
     * 功能描述: 校验验证码是否正确
     *
     * @author wlf 2024/6/3 22:15
     */
    private void checkCode(String loginName, String code) {
        String str = mailService.getOpsValue().get(loginName);
        if (!code.equals(str)) {
            CommonResult.error("验证码错误,请核对验证码是否有误");
        }
        // 验证成功删除
        stringRedisTemplate.delete(loginName);

    }

    /**
     * 功能描述: 校验邮箱以及验证码信息
     *
     * @author wlf 2024/6/2 16:54
     */
    private void checkEmailAndCaptcha(String id, String email) {
        if (imageCaptchaApplication instanceof SecondaryVerificationApplication) {
            boolean verification = ((SecondaryVerificationApplication) imageCaptchaApplication)
                    .secondaryVerification(id == null ? "0" : id);
            if (!verification) {
                CommonResult.error("请校验验证码!");
            }
        } else {
            CommonResult.error("请开启二次验证!");
        }
        if (!Validator.isEmail(email)) {
            CommonResult.error("请输入正确的邮箱");
        }
    }


    /**
     * 功能描述: 验证码登录
     *
     * @author wlf 2024/4/23 20:36
     */
    @Getter
    @Setter
    static class CodeLoginParam {
        // 验证码返回ID
        private String id;
        //
        // 邮箱
        private String loginName;
        // 验证码
        private String code;
    }

    @Operation(summary = "验证码登录", description = "验证码登录")
    @PostMapping("/code-login")
    CommonResult<TokenVO> codeLogin(@RequestBody CodeLoginParam param) {

        checkCode(param.loginName, param.code);
        // 登录具体操作
        StpUtil.login(handleUser(param.loginName, null),
                SaLoginModel.create()
                        .setDevice(DeviceEnum.PC.name()));
        // 返回Token信息
        TokenVO tokenVo = BeanUtil.copyProperties(StpUtil.getTokenInfo(), TokenVO.class);
        List<String> permissionList = StpUtil.getPermissionList();
        tokenVo.setPowerList(permissionList);
        return CommonResult.data(tokenVo);
    }


    /**
     * 功能描述: 验证码登录
     *
     * @author wlf 2024/4/23 20:36
     */
    @Getter
    @Setter
    static class ChangePdParam {
        // 邮箱
        private String loginName;
        // 验证码
        private String code;
        // 密码1
        private String passwd;
        // 密码2
        private String passwd2;
    }

    @Operation(summary = "忘记密码修改密码登录", description = "忘记密码修改密码登录")
    @PostMapping("/set-passwd-login")
    CommonResult<TokenVO> setPasswdLogin(@RequestBody ChangePdParam param) {
        if (StrUtil.isBlank(param.getPasswd())) {
            CommonResult.error("密码不可为空");
        }
        if (!param.getPasswd().equals(param.passwd2)) {
            CommonResult.error("两次输入密码不一致");
        }
        if (!YuanWebUtil.checkPd(param.getPasswd())) {
            CommonResult.error("密码只能为字母，数字，下划线");
        }

        checkCode(param.loginName, param.code);

        // 登录具体操作
        StpUtil.login(handleUser(param.loginName, param.passwd),
                SaLoginModel.create()
                        .setDevice(DeviceEnum.PC.name()));
        // 返回Token信息
        TokenVO tokenVo = BeanUtil.copyProperties(StpUtil.getTokenInfo(), TokenVO.class);
        List<String> permissionList = StpUtil.getPermissionList();
        tokenVo.setPowerList(permissionList);
        return CommonResult.data(tokenVo);
    }

    // 保存用户授权信息
    private void saveAuthUser(Long userId, String passwd) {
        String salt = RandomUtil.randomString(6);
        Db.save(UserAuth.builder()
                .userId(userId)
                .salt(salt)
                .passwd(passwd == null ? null : AesUtil.enc(salt + passwd))
                .lastLoginTime(LocalDateTime.now())
                .build());
    }

    private Long handleUser(String loginName, String passwd) {
        UserInfo userInfo = Db.lambdaQuery(UserInfo.class)
                .eq(UserInfo::getEmail, loginName)
                .ne(UserInfo::getUserStatus, UserStatusEnum.del.getCode())
                .one();
        // 如果库中无此邮箱，则写入一条用户信息
        if (userInfo == null) {
            userInfo = new UserInfo();
            userInfo.setId(IdWorker.getId());
            userInfo.setEmail(loginName);
            userInfo.setUsername("用户-" + YuanWebUtil.randNumStr(6));
            // 默认头像
            userInfo.setAvatar(YuanWebConst.defaultAvatar);
            // 保存用户
            Db.save(userInfo);
            // 保存UserAuth信息
            saveAuthUser(userInfo.getId(), passwd);
            Db.save(UserSetting.builder()
                    .userId(userInfo.getId())
                    .build());
        } else {
            if (userInfo.getUserStatus().equals(UserStatusEnum.disable)) {
                CommonResult.error("账号已被禁用");
            }
            // 存在用户更新最后登录时间
            Db.lambdaUpdate(UserAuth.class)
                    .eq(UserAuth::getUserId, userInfo.getId())
                    .set(UserAuth::getLastLoginTime, LocalDateTime.now())
                    .update();
        }
        return userInfo.getId();

    }

    @Operation(summary = "注销登录")
    @GetMapping("/logout")
    CommonResult<?> logout() {
        StpUtil.logout();
        return CommonResult.ok();
    }


}
